class Counter {
  count = Date.now();

  getNext() {
    return this.count++;
  }
}

const randomIdCounter = new Counter();
const randomNumberCounter = new Counter();

function genRandomId() {
  const mid = randomIdCounter
    .getNext()
    .toString()
    .split("")
    .map((num) => {
      return String.fromCharCode(97 + parseInt(num)) + num;
    })
    .join("");
  return mid;
}

/**
 * @description 获取指定范围内的随机整数, 包括最小值和最大值
 * @description_en Get a random integer within a specified range, including the minimum and maximum values
 */
export function getRandomIntInclusive(min: number, max: number): number {
  if (min >= max) {
    throw new Error("min must be less than or equal to max");
  }
  const minCeil = Math.ceil(min);
  const maxFloor = Math.floor(max);
  return Math.floor(Math.random() * (maxFloor - minCeil + 1)) + minCeil;
}

export const decode = <T = any>(content: any): T => {
  if (!content) return {} as T;

  if (typeof content === "string") {
    try {
      return JSON.parse(content);
    } catch (e) {
      console.error("decode error", e);
      return {} as T;
    }
  }

  return content;
};

export const decodeListItem = (message: any) => {
  if (!message) return message;
  try {
    message.content = decode(message.content);
    if (message.extra) message.extra = decode(message.extra);
    return message;
  } catch (err) {
    console.error("decodeListItem error", err);
    return message;
  }
};

const chatLength = 20;
const senderLength = 100;
const typeLength = 20;
const statusLength = 6;
export const allChatIds = Array.from(
  { length: chatLength },
  (_, i) => `c${i + 1}`
);
const allAChatIds = allChatIds.map((chatId) => `a${chatId}`);
const allSenders = Array.from({ length: 100 }, (_, i) => `s${i + 1}`);
const allTypes = Array.from({ length: typeLength }, (_, i) => i + 1);
const allStatus = Array.from({ length: statusLength }, (_, i) => i + 1);
export const MyInfo = {
  id: "s10",
  name: "啦啦啦",
};

export function genMessage() {
  const randomMId = genRandomId();
  const chatId = "c" + getRandomIntInclusive(1, chatLength);
  const aChatId = `a${chatId}`;
  const sender = "s" + getRandomIntInclusive(1, senderLength);
  const number = randomNumberCounter.getNext();
  const now = Date.now();
  const sequence = number - 100000;
  return {
    mid: randomMId,
    amid: randomMId,
    chatId,
    aChatId,
    content: {
      text: `mid:${randomMId} , number:${number} , sequence:${sequence} now: ${now} , sender:${sender} - testMessage`,
    },
    type: getRandomIntInclusive(1, typeLength),
    status: getRandomIntInclusive(1, statusLength),
    createAt: now,
    updateAt: now,
    extra: {
      qMid: "q" + randomMId,
    },
    sender,
    isRead: getRandomIntInclusive(0, 1),
    deleteFlag: getRandomIntInclusive(0, 1),
    number,
    sequence,
    timestamp: now,
    ep: 0,
    at: 0,
  };
}

function connectDB() {
  return new Promise<IDBDatabase>((resolve, reject) => {
    const request = indexedDB.open("test", 1);
    request.onupgradeneeded = function () {
      const db = request.result;
      const store = db.createObjectStore("messages", {
        keyPath: "id",
        autoIncrement: true,
      });
      store.createIndex("mid", "mid", { unique: true });
      store.createIndex("amid", "amid", { unique: false });
      store.createIndex("chatId", "chatId", { unique: false });
      store.createIndex("number", "number", { unique: false });
      store.createIndex("sequence", "sequence", { unique: false });
      store.createIndex("sender", "sender", { unique: false });
      store.createIndex("sendTime", "sendTime", { unique: false });
      store.createIndex("status", "status", { unique: false });
      store.createIndex("type", "type", { unique: false });
      store.createIndex("timestamp", "timestamp", { unique: false });
    };
    request.onsuccess = function () {
      resolve(request.result);
    };
    request.onerror = function () {
      reject(request.error);
    };
  });
}

function putMsgs(db: IDBDatabase, items: any[]) {
  return new Promise<void>((resolve, reject) => {
    const transaction = db.transaction("messages", "readwrite");
    const store = transaction.objectStore("messages");
    const index = store.index("mid");

    const checkAndAdd = (item: any) => {
      return new Promise<void>((resolve, reject) => {
        const request = index.get(item.mid);
        request.onsuccess = function () {
          if (!request.result) {
            store.add(item);
          }
          resolve();
        };
        request.onerror = function () {
          reject(request.error);
        };
      });
    };

    const promises = items.map((item) => checkAndAdd(item));
    Promise.all(promises)
      .then(
        () =>
          (transaction.oncomplete = function () {
            resolve();
          })
      )
      .catch(
        (error) =>
          (transaction.onerror = function () {
            reject(error);
          })
      );
  });
}

function getMsgByMid(db: IDBDatabase, mid: string) {
  return new Promise<any>((resolve, reject) => {
    const transaction = db.transaction("messages", "readonly");
    const store = transaction.objectStore("messages");
    const index = store.index("mid");
    const request = index.get(mid);
    request.onsuccess = function () {
      resolve(request.result);
    };
    request.onerror = function () {
      reject(request.error);
    };
  });
}

function deleteChatMsg(db: IDBDatabase, chatId: string) {
  return new Promise<number>((resolve, reject) => {
    const transaction = db.transaction("messages", "readwrite");
    const store = transaction.objectStore("messages");
    const index = store.index("chatId");
    const request = index.openCursor(IDBKeyRange.only(chatId));
    let count = 0;
    request.onsuccess = function () {
      const cursor = request.result;
      if (cursor) {
        cursor.delete();
        count++;
        cursor.continue();
      } else {
        resolve(count);
      }
    };
    request.onerror = function () {
      reject(request.error);
    };
  });
}

function getUnreadCountByChatId(db: IDBDatabase, chatId: string) {
  return new Promise<number>((resolve, reject) => {
    const transaction = db.transaction("messages", "readonly");
    const store = transaction.objectStore("messages");
    const index = store.index("chatId");
    const request = index.openCursor(IDBKeyRange.only(chatId));
    let count = 0;
    request.onsuccess = function () {
      const cursor = request.result;
      if (cursor) {
        if (cursor.value.isRead === 0) {
          count++;
        }
        cursor.continue();
      } else {
        resolve(count);
      }
    };
    request.onerror = function () {
      reject(request.error);
    };
  });
}

function readChatAllMsg(db: IDBDatabase, chatId: string) {
  return new Promise<{
    updateCount: number;
    allCount: number;
  }>((resolve, reject) => {
    const transaction = db.transaction("messages", "readwrite");
    const store = transaction.objectStore("messages");
    const index = store.index("chatId");
    const request = index.openCursor(IDBKeyRange.only(chatId));
    let upCount = 0;
    let allCount = 0;
    request.onsuccess = function () {
      const cursor = request.result;
      if (cursor) {
        if (cursor.value.isRead === 0) {
          cursor.value.isRead = 1;
          upCount++;
          store.put(cursor.value);
        }
        allCount++;
        cursor.continue();
      } else {
        resolve({
          updateCount: upCount,
          allCount,
        });
      }
    };
    request.onerror = function () {
      reject(request.error);
    };
  });
}

export function groupUnreadCountByChatId(db: IDBDatabase) {
  const map = allChatIds.reduce((acc, chatId) => {
    acc[chatId] = 0;
    return acc;
  }, {} as Record<string, number>);

  return new Promise<Record<string, number>>((resolve, reject) => {
    const transaction = db.transaction("messages", "readonly");
    const store = transaction.objectStore("messages");
    const request = store.openCursor();
    request.onsuccess = function () {
      const cursor = request.result;
      if (cursor) {
        if (cursor.value.isRead === 0) {
          map[cursor.value.chatId]++;
        }
        cursor.continue();
      } else {
        resolve(map);
      }
    };
    request.onerror = function () {
      reject(request.error);
    };
  });
}

export function pagingQueryByChatId(
  db: IDBDatabase,
  chatId: string,
  options: {
    limit: number;
    skipCount: number;
    filter?: (msg: any) => boolean;
    reverse?: boolean;
  }
) {
  const p1 = performance.now();
  const { reverse = true, limit, skipCount, filter } = options;
  const transaction = db.transaction(["messages"], "readonly");
  const store = transaction.objectStore("messages");
  const index = store.index("chatId");
  const range = IDBKeyRange.only(chatId);
  const request = index.openCursor(range, reverse ? "prev" : "next");

  // 游标扫描的次数
  let scanCount = skipCount;
  // 是否跳过
  let isSkipped = false;
  const result: any[] = [];

  return new Promise<{
    count: number;
    data: any[];
    scanCount: number;
    isEnd: boolean;
  }>((resolve, reject) => {
    request.onsuccess = function (event) {
      //@ts-ignore
      const cursor = event.target!.result;

      if (cursor) {
        if (skipCount && !isSkipped) {
          cursor.advance(skipCount);
          isSkipped = true; // 更新跳过状态
          return;
        }
        scanCount++;
        const msg = cursor.value;
        if (msg.deleteFlag === 1) {
          cursor.continue();
          return;
        }
        if (filter && !filter(msg)) {
          cursor.continue();
          return;
        }
        result.push(decodeListItem(msg));
        if (result.length < limit) {
          cursor.continue();
          return;
        } else {
          console.log(
            "分页查询",
            {
              chatId,
              limit,
              skipCount,
            },
            "耗时",
            performance.now() - p1 + "ms"
          );
          resolve({
            count: result.length,
            data: result,
            scanCount,
            isEnd: false,
          });
          return;
        }
      } else {
        console.log(
          "分页查询",
          {
            chatId,
            limit,
            skipCount,
          },
          "耗时",
          performance.now() - p1 + "ms"
        );
        resolve({
          count: result.length,
          data: result,
          scanCount,
          isEnd: true,
        });
      }
    };
    request.onerror = function (event) {
      //@ts-ignore
      reject(new Error(`IndexedDB request error: ${event.target.error}`)); // 返回详细错误信息
    };
  });
}

//获取id位于start和end之间的某个chatId消息未删除的消息
function getMany1(
  db: IDBDatabase,
  chatId: string,
  start = 400000,
  end = 900000
) {
  return new Promise<any[]>((resolve, reject) => {
    const transaction = db.transaction("messages", "readonly");
    const store = transaction.objectStore("messages");
    const index = store.index("chatId");
    const request = index.openCursor(IDBKeyRange.only(chatId));
    const msgs: any[] = [];
    request.onsuccess = function () {
      const cursor = request.result;
      if (cursor) {
        if (
          cursor.value.id >= start &&
          cursor.value.id <= end &&
          cursor.value.deleteFlag === 0
        ) {
          msgs.push(cursor.value);
        }
        cursor.continue();
      } else {
        resolve(msgs.sort((a, b) => a.id - b.id));
      }
    };
    request.onerror = function () {
      reject(request.error);
    };
  });
}

function deleteByChatIdWithType(db: IDBDatabase, chatId: string, type: number) {
  return new Promise<number>((resolve, reject) => {
    const transaction = db.transaction("messages", "readwrite");
    const store = transaction.objectStore("messages");
    const index = store.index("chatId");
    const request = index.openCursor(IDBKeyRange.only(chatId));
    let count = 0;
    request.onsuccess = function () {
      const cursor = request.result;
      if (cursor) {
        if (cursor.value.type === type) {
          cursor.delete();
          count++;
        }
        cursor.continue();
      } else {
        resolve(count);
      }
    };
    request.onerror = function () {
      reject(request.error);
    };
  });
}

declare global {
  interface Window {
    putManyMsg: (count: number) => Promise<void>;
    putMsgs: (items: any[]) => Promise<void>;
    getMsgByMid: (mid: string) => Promise<void>;
    deleteMsgByChatId: (chatId: string) => Promise<void>;
    getUnreadCountByChatId: (chatId: string) => Promise<void>;
    readChatAllMsg: (chatId: string) => Promise<void>;
    getMany1: (chatId: string, start: number, end: number) => Promise<void>;
    startDelTask: (chatId: string, interval: number) => void;
    deleteByChatIdWithType: (chatId: string, type: number) => Promise<void>;
  }
}

export async function getDB() {
  const db = await connectDB();
  console.log("allChatIds", allChatIds);
  console.log("allAChatIds", allAChatIds);
  console.log("allSenders", allSenders);
  console.log("allTypes", allTypes);
  console.log("allStatus", allStatus);
  window.putMsgs = async (items: any[]) => {
    const p1 = performance.now();
    await putMsgs(db, items);
    const p2 = performance.now();
    console.log(`put ${items.length} messages 耗时 ${p2 - p1} 毫秒`);
  };
  console.log("插入数据方法: window.putManyMsg(count: number)");
  window.putManyMsg = async (count: number) => {
    const g1 = performance.now();
    let items = [];
    const chunkSize = 1000;

    for (let i = 0; i < count; i++) {
      items.push(genMessage());
      if (items.length === chunkSize) {
        const c1 = performance.now();
        await putMsgs(db, items);
        const c2 = performance.now();
        console.log(`put chunk ${chunkSize} 耗时 ${c2 - c1} 毫秒`);
        items = [];
      }
    }

    if (items.length > 0) {
      const p1 = performance.now();
      await putMsgs(db, items);
      const p2 = performance.now();
      console.log(`put lastChunk${items.length} 耗时 ${p2 - p1} 毫秒`);
    }

    const g2 = performance.now();
    console.log(`put ${count} messages 总耗时 ${g2 - g1} 毫秒`);
  };
  console.log("按chatId删除会话消息: window.deleteMsgByChatId(chatId:string)");
  window.deleteMsgByChatId = async (chatId: string) => {
    const g1 = performance.now();
    const length = await deleteChatMsg(db, chatId);
    const g2 = performance.now();
    console.log(
      `delete chatId ${chatId},条数:${length} 总耗时 ${g2 - g1} 毫秒`
    );
  };
  console.log("获取未读消息数: getUnreadCountByChatId(chatId:string)");
  window.getUnreadCountByChatId = async (chatId: string) => {
    const p1 = performance.now();
    const count = await getUnreadCountByChatId(db, chatId);
    const p2 = performance.now();
    console.log(`chatId ${chatId} 未读消息数: ${count} 总耗时 ${p2 - p1} 毫秒`);
  };
  console.log(
    "将chatId对应的消息所有未读消息设置为已读: readChatAllMsg(chatId:string)"
  );
  window.readChatAllMsg = async (chatId: string) => {
    const p1 = performance.now();
    const { updateCount, allCount } = await readChatAllMsg(db, chatId);
    const p2 = performance.now();
    console.log(
      `chatId ${chatId} 更新消息数: ${updateCount} 总消息数: ${allCount} 总耗时 ${
        p2 - p1
      } 毫秒`
    );
  };
  console.log(
    "获取id位于start和end之间的某个chatId消息未删除的消息: getMany1(chatId:string,start:number,end:number)"
  );
  window.getMany1 = async (chatId: string, start = 400000, end = 900000) => {
    const p1 = performance.now();
    const msgs = await getMany1(db, chatId, start, end);
    const p2 = performance.now();
    console.log(
      `chatId ${chatId} id位于${start}和${end}之间的消息数且已读的消息 总耗时 ${
        p2 - p1
      } 毫秒`
    );
    console.log(msgs);
  };
  console.log("通过mid获取消息", "window.getMsgByMid(mid: string)");
  window.getMsgByMid = async (mid: string) => {
    const p1 = performance.now();
    const msg = await getMsgByMid(db, mid);
    const p2 = performance.now();
    console.log(`mid ${mid} 查询耗时 ${p2 - p1} 毫秒`);
    return msg;
  };
  console.log(
    "按chatId和type删除消息: deleteByChatIdWithType(chatId:string,type:number)"
  );
  window.deleteByChatIdWithType = async (chatId: string, type: number) => {
    const p1 = performance.now();
    const length = await deleteByChatIdWithType(db, chatId, type);
    const p2 = performance.now();
    console.log(
      `删除消息: chatId ${chatId} type ${type},条数:${length} 总耗时 ${
        p2 - p1
      } 毫秒`
    );
  };

  return db;
}
